package com.alibaba.citrus.cr.unified.inventory.repository.impl;

import com.alibaba.citrus.cr.common.exception.FacadeException;
import com.alibaba.citrus.cr.common.util.JsonUtil;
import com.alibaba.citrus.cr.common.util.PageUtils;
import com.alibaba.citrus.cr.unified.inventory.repository.AllocateApplyOrderReadRepository;
import com.alibaba.citrus.cr.unified.inventory.utils.OtsDateUtils;
import com.alibaba.cz.base.tool.Nullable;
import com.alibaba.cz.base.tool.feature.Features;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.AllocateApplyOrderLoadListRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.GetListRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.GetListResponse;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.LoadAllocateApplyOrderByCodeRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.LoadAllocateApplyOrderByOutAllocateApplyOrderIdRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.dto.LoadAllocateApplyOrderRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.model.AllocateApplyOrder;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorder.service.AllocateApplyOrderService;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorderdetail.dto.QueryListForStatCreditRequest;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorderdetail.model.AllocateApplyOrderDetail;
import com.epoch.app.crunifiedinventory.domain.allocateapplyorderdetail.service.AllocateApplyOrderDetailService;
import com.epoch.app.crunifiedinventory.dto.response.AllocateApplyOrderDetailDTO;
import com.epoch.app.crunifiedinventory.dto.response.AllocateApplyOrderListDTO;
import com.epoch.app.crunifiedinventory.dto.response.AllocateApplyOrderResponse;
import com.epoch.app.crunifiedinventory.model.dto.AllocateApplyOrderCodeQueryRequest;
import com.epoch.app.crunifiedinventory.model.dto.AllowPlacedOrderAmountResponse;
import com.epoch.app.crunifiedinventory.sdo.AllocateApplyOrderSDO;
import com.epoch.app.crunifiedinventory.sdo.enums.AllocateApplyOrderStatusEnum;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;


/**
 * @author heyu
 * @description
 */
@Component
public class AllocateApplyOrderReadRepositoryImpl implements AllocateApplyOrderReadRepository {

    @Resource
    private AllocateApplyOrderService allocateApplyOrderService;

    @Resource
    private AllocateApplyOrderDetailService allocateApplyOrderDetailService;

    @Resource(name = "logsCheckSyncTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private static final Integer MAX_PAGE_SIZE = 1000;
    private static Log log = Log.getLogger(AllocateApplyOrderReadRepositoryImpl.class);

    @Override
    public List<AllocateApplyOrderListDTO> queryAllocateApplyByAllocateApplyOrderIds(List<String> allocateApplyOrderIds) {
        List<AllocateApplyOrderListDTO> allocateApplyOrderListDTOS = new ArrayList<>();
        LoadAllocateApplyOrderRequest loadAllocateApplyOrderRequest = new LoadAllocateApplyOrderRequest();
        if (CollectionUtils.isNotEmpty(allocateApplyOrderIds)){
            for (String allocateApplyOrderId:allocateApplyOrderIds){
                loadAllocateApplyOrderRequest.setId(allocateApplyOrderId);
                AllocateApplyOrder allocateApplyOrder = allocateApplyOrderService.loadAllocateApplyOrder(loadAllocateApplyOrderRequest);
                AllocateApplyOrderListDTO allocateApplyOrderListDTO = new AllocateApplyOrderListDTO();
                allocateApplyOrderListDTO.setId(allocateApplyOrder.getId());
                allocateApplyOrderListDTO.setCode(allocateApplyOrder.getCode());
                allocateApplyOrderListDTO.setType(allocateApplyOrder.getType());
                allocateApplyOrderListDTO.setApplyOrg(allocateApplyOrder.getApplyOrgId() == null ? null : allocateApplyOrder.getApplyOrgId().toString());
//                allocateApplyOrderListDTO.setReceiveWarehouse(allocateApplyOrder.getReceiveWarehouseId()!=null?allocateApplyOrder.getReceiveWarehouseId().toString():"");
                allocateApplyOrderListDTO.setCommitTime(allocateApplyOrder.getCommitTime());
//                allocateApplyOrderListDTO.setReplyTime(allocateApplyOrder.getConfirmTime());
                //TODO 提交人不确定，取的创建人
                JSONObject creator = JSON.parseObject(allocateApplyOrder.getCreator());
                if(Objects.nonNull(creator)){
                    allocateApplyOrderListDTO.setSubmitter(creator.size()==0?"":creator.getString("stageName"));
                }
//                allocateApplyOrderListDTO.setDeliverWarehouse(allocateApplyOrder.getShipWarehouseId()!=null?allocateApplyOrder.getShipWarehouseId().toString():"");
                //TODO 暂取sap预调拨单号
                allocateApplyOrderListDTO.setAllocateOrderNo(allocateApplyOrder.getOutAllocateApplyOrderId());
                allocateApplyOrderListDTO.setStatus(allocateApplyOrder.getStatus());
                allocateApplyOrderListDTOS.add(allocateApplyOrderListDTO);

            }
        }
        return allocateApplyOrderListDTOS;
    }

    @Override
    public AllocateApplyOrderResponse queryAllocateApplyOrderByCode(String code) {
        AllocateApplyOrderResponse allocateApplyOrderResponse = new AllocateApplyOrderResponse();
        LoadAllocateApplyOrderByCodeRequest loadAllocateApplyOrderByCodeRequest = new LoadAllocateApplyOrderByCodeRequest();
        loadAllocateApplyOrderByCodeRequest.setCode(code);
        AllocateApplyOrder allocateApplyOrder = allocateApplyOrderService.loadAllocateApplyOrderByCode(loadAllocateApplyOrderByCodeRequest);
        AllocateApplyOrderDetailDTO allocateApplyOrderDetailDTO = new AllocateApplyOrderDetailDTO();
        BeanUtils.copyProperties(allocateApplyOrder,allocateApplyOrderDetailDTO);
        allocateApplyOrderDetailDTO.setApplyOrg(allocateApplyOrder.getApplyOrgId() == null ? null : allocateApplyOrder.getApplyOrgId().toString());
        allocateApplyOrderDetailDTO.setType(allocateApplyOrder.getType() == null? null: allocateApplyOrder.getType().toString());
        allocateApplyOrderDetailDTO.setShipMethod(allocateApplyOrder.getShipMethod());
        allocateApplyOrderDetailDTO.setReceiveWarehouseId(Objects.isNull(allocateApplyOrder.getReceiveWarehouseId())?"":allocateApplyOrder.getReceiveWarehouseId().toString());
        allocateApplyOrderDetailDTO.setAddresscode(allocateApplyOrder.getCustomerAddress());
        allocateApplyOrderDetailDTO.setGmtCreate(allocateApplyOrder.getGmt_create());
        allocateApplyOrderDetailDTO.setShipWarehouseId(null==allocateApplyOrder.getShipWarehouseId()?"":allocateApplyOrder.getShipWarehouseId().toString());
        //单据日期
        allocateApplyOrderDetailDTO.setOrderTime(getStringDate(allocateApplyOrder.getOrderTime()));
        //生效日期
        allocateApplyOrderDetailDTO.setTakeEffectTime(getStringDate(allocateApplyOrder.getTakeEffectTime()));
        //生效至
        allocateApplyOrderDetailDTO.setInvalidDate(getStringDate(allocateApplyOrder.getInvalidDate()));
        //期望交货日期
        allocateApplyOrderDetailDTO.setHopeDeliveryTime(getStringDate(allocateApplyOrder.getHopeDeliveryTime()));
        String featureStr = allocateApplyOrder.getBizFeatures();
        Features features = Features.newFeatures(featureStr);
        allocateApplyOrderDetailDTO.setOrderTag(features.get("order_tag"));
        allocateApplyOrderDetailDTO.setBizUnit(features.get("biz_unit"));
        allocateApplyOrderResponse.setAllocateApplyOrderDetailDTO(allocateApplyOrderDetailDTO);
        return allocateApplyOrderResponse;
    }

    @Override
    public AllocateApplyOrderResponse queryAllocateApplyOrderByCodeAndFromSysAndBusiness(String code,String fromSys,String businessType) {
        AllocateApplyOrderResponse allocateApplyOrderResponse = new AllocateApplyOrderResponse();
        LoadAllocateApplyOrderByCodeRequest loadAllocateApplyOrderByCodeRequest = new LoadAllocateApplyOrderByCodeRequest();
        loadAllocateApplyOrderByCodeRequest.setCode(code);
        loadAllocateApplyOrderByCodeRequest.setFromSys(fromSys);
        loadAllocateApplyOrderByCodeRequest.setBusinessType(businessType);
        AllocateApplyOrder allocateApplyOrder = allocateApplyOrderService.loadAllocateApplyOrderByCode(loadAllocateApplyOrderByCodeRequest);
        AllocateApplyOrderDetailDTO allocateApplyOrderDetailDTO = new AllocateApplyOrderDetailDTO();
        BeanUtils.copyProperties(allocateApplyOrder,allocateApplyOrderDetailDTO);
        allocateApplyOrderDetailDTO.setApplyOrg(allocateApplyOrder.getApplyOrgId() == null ? null : allocateApplyOrder.getApplyOrgId().toString());
        allocateApplyOrderDetailDTO.setType(allocateApplyOrder.getType() == null? null: allocateApplyOrder.getType().toString());
        allocateApplyOrderDetailDTO.setShipMethod(allocateApplyOrder.getShipMethod());
        allocateApplyOrderDetailDTO.setReceiveWarehouseId(Objects.isNull(allocateApplyOrder.getReceiveWarehouseId())?"":allocateApplyOrder.getReceiveWarehouseId().toString());
        allocateApplyOrderDetailDTO.setAddresscode(allocateApplyOrder.getCustomerAddress());
        allocateApplyOrderDetailDTO.setGmtCreate(allocateApplyOrder.getGmt_create());
        allocateApplyOrderDetailDTO.setShipWarehouseId(null==allocateApplyOrder.getShipWarehouseId()?"":allocateApplyOrder.getShipWarehouseId().toString());
        //单据日期
        allocateApplyOrderDetailDTO.setOrderTime(getStringDate(allocateApplyOrder.getOrderTime()));
        //生效日期
        allocateApplyOrderDetailDTO.setTakeEffectTime(getStringDate(allocateApplyOrder.getTakeEffectTime()));
        //生效至
        allocateApplyOrderDetailDTO.setInvalidDate(getStringDate(allocateApplyOrder.getInvalidDate()));
        //期望交货日期
        allocateApplyOrderDetailDTO.setHopeDeliveryTime(getStringDate(allocateApplyOrder.getHopeDeliveryTime()));
        allocateApplyOrderResponse.setAllocateApplyOrderDetailDTO(allocateApplyOrderDetailDTO);
        return allocateApplyOrderResponse;
    }

    private static String getStringDate(Date date){
        String s = "";
        if (Objects.nonNull(date)) {
            s = OtsDateUtils.formatDate(date, OtsDateUtils.DATE_FORMAT);
        }
        return s;
    }
    @Override
    public List<AllocateApplyOrderSDO> getAllocateApplyOrderSdoListByIds(List<Long> idList) {
        GetListRequest r = new GetListRequest();
        if (CollectionUtils.isNotEmpty(idList)){
            r.setIdList(idList);
        }
        r.setLimit(10000);
        r.setStart(0);
        GetListResponse response =
        allocateApplyOrderService.getList(r);

        return JsonUtil.convertList(response.getResult(), AllocateApplyOrderSDO.class);
    }

    @Override
    public AllocateApplyOrderSDO query(AllocateApplyOrderCodeQueryRequest request) {
        if (request != null && StringUtils.isNotBlank(request.getAllocateApplyOrderCode())){
            AllocateApplyOrderLoadListRequest r = new AllocateApplyOrderLoadListRequest();
            r.setCode(request.getAllocateApplyOrderCode());
            Result<List<AllocateApplyOrder>> result = allocateApplyOrderService.loadAllocateApplyOrderList(r);
            if (result != null && CollectionUtils.isNotEmpty(result.getResult())){
                return JsonUtil.convert(result.getResult().get(0),AllocateApplyOrderSDO.class);
            }
        }
        return null;
    }

    @Override
    public Result<String> getAllocateApplyOrderIdByOutAllocateApplyOrderId(String outAllocateApplyOrderId) {
        LoadAllocateApplyOrderByOutAllocateApplyOrderIdRequest request = new LoadAllocateApplyOrderByOutAllocateApplyOrderIdRequest();
        request.setOutAllocateApplyOrderId(outAllocateApplyOrderId);
        Result<List<AllocateApplyOrder>> listResult = allocateApplyOrderService.loadAllocateApplyOrderByOutAllocateApplyOrderId(request);
        Result<String> result = new Result();
        if (Objects.isNull(listResult)){
            result.setSuccess(false);
            return result;
        }
        if (CollectionUtils.isEmpty(listResult.getResult())){
            result.setSuccess(false);
            return result;
        }
        result.setResult(listResult.getResult().get(0).getCode());
        result.setSuccess(false);
        return result;
    }

    //查询可下预报单订单金额
    @Override
    public Result<AllowPlacedOrderAmountResponse> queryAllowPlacedOrderAmount(String companyId) {
        if (StringUtils.isBlank(companyId)){
//            return Result.fail("OTS-03-005-00-15-400","分子公司id不能为空");
            throw new FacadeException("OTS-03-005-00-15-400");
        }
        //查询调拨预报单行状态是待回复的
        List<AllocateApplyOrderDetail> waitReplyDetails = queryAllocateApplyOrderDetails(companyId, AllocateApplyOrderStatusEnum.WAIT_ANSWER.getCode());
        log.info("waitReplyDetails:{}",JSON.toJSONString(waitReplyDetails));
        //查询调拨预报单行状态是部分回复的
        List<AllocateApplyOrderDetail> partReplyDetails = queryAllocateApplyOrderDetails(companyId, AllocateApplyOrderStatusEnum.PART_ANSWER.getCode());
        log.info("partReplyDetails:{}",JSON.toJSONString(partReplyDetails));
        String pendingReplyOrderAmount = calcPendingReplyOrderAmount(waitReplyDetails);
        String partReplyOrderAmount = calcPartReplyOrderAmount(partReplyDetails);
        AllowPlacedOrderAmountResponse allowPlacedOrderAmountResponse = new AllowPlacedOrderAmountResponse();
        allowPlacedOrderAmountResponse.setPendingReplyOrderAmount(pendingReplyOrderAmount);
        allowPlacedOrderAmountResponse.setPartReplyOrderAmount(partReplyOrderAmount);
        log.info("allowPlacedOrderAmountResponse:{}",JSON.toJSONString(allowPlacedOrderAmountResponse));
        return Result.success(allowPlacedOrderAmountResponse);
    }

    private List<AllocateApplyOrderDetail> queryAllocateApplyOrderDetails(String companyId,Integer status){
        QueryListForStatCreditRequest queryListForStatCreditRequest = new QueryListForStatCreditRequest();
        queryListForStatCreditRequest.setCompanyId(Long.parseLong(companyId));
        queryListForStatCreditRequest.setStatus(status);
        queryListForStatCreditRequest.setStart(0);
        queryListForStatCreditRequest.setLimit(MAX_PAGE_SIZE);
        Result<List<AllocateApplyOrderDetail>> listResult = allocateApplyOrderDetailService.queryListForStatCredit(queryListForStatCreditRequest);
        if (listResult == null || CollectionUtils.isEmpty(listResult.getResult())){
            return Lists.newArrayList();
        }
        List<AllocateApplyOrderDetail> dataList = Lists.newArrayList();
        dataList.addAll(listResult.getResult());
        int total = listResult.getTotal();
        if (total > MAX_PAGE_SIZE){
            int page = PageUtils.getTotalPage(total,MAX_PAGE_SIZE);
            List<Future<List<AllocateApplyOrderDetail>>> futureList = Lists.newArrayList();
            for (int i = 1; i <= page; i++) {
                int finalPage = i;
                CompletableFuture<List<AllocateApplyOrderDetail>> future = CompletableFuture.supplyAsync(() -> {
                    queryListForStatCreditRequest.setStart(finalPage * MAX_PAGE_SIZE);
                    Result<List<AllocateApplyOrderDetail>> result = allocateApplyOrderDetailService.queryListForStatCredit(queryListForStatCreditRequest);
                    if (result != null && CollectionUtils.isNotEmpty(result.getResult())){
                        return result.getResult();
                    }
                    return null;
                },threadPoolTaskExecutor);
                futureList.add(future);
            }
            Nullable.stream(futureList).forEach(listFuture -> {
                try {
                    List<AllocateApplyOrderDetail> list = listFuture.get();
                    if (CollectionUtils.isNotEmpty(list)){
                        dataList.addAll(list);
                    }
                }catch (Throwable e){
                    log.error("queryAllowPlacedOrderAmount_error",e);
                }
            });
        }
        return dataList;
    }

    //待回复订单金额
    private String calcPendingReplyOrderAmount(List<AllocateApplyOrderDetail> details){
        return Nullable.stream(details).map(detail -> {
            if (detail.getSubtotalMoney() != null){
                return detail.getSubtotalMoney();
            }else {
                if (detail.getUnitPrice() == null || detail.getApplyQuantity() == null){
                    return BigDecimal.ZERO;
                }
                return detail.getUnitPrice().multiply(new BigDecimal(detail.getApplyQuantity()));
            }
        }).reduce(BigDecimal.ZERO, BigDecimal::add).toPlainString();
    }
    //部分回复订单金额
    private String calcPartReplyOrderAmount(List<AllocateApplyOrderDetail> details){
        return Nullable.stream(details).map(detail -> {
            if (detail.getApplyQuantity() == null){
                return BigDecimal.ZERO;
            }
            BigDecimal applyQuantity = new BigDecimal(detail.getApplyQuantity());
            BigDecimal replyQuantity = detail.getReplyQuantity() == null ? BigDecimal.ZERO : new BigDecimal(detail.getReplyQuantity());
            BigDecimal unansweredQuantity = applyQuantity.subtract(replyQuantity);
            if (detail.getSubtotalMoney() != null){
                return detail.getSubtotalMoney().divide(applyQuantity,5,RoundingMode.HALF_UP).multiply(unansweredQuantity);
            }else {
                if (detail.getUnitPrice() == null){
                    return BigDecimal.ZERO;
                }
                return detail.getUnitPrice().multiply(unansweredQuantity);
            }
        }).reduce(BigDecimal.ZERO, BigDecimal::add).toPlainString();
    }
}
